// 版权所有2011 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:build drangfly | | freebsd | | solaris 
// /+build drangfly freebsd solaris 

package net

import (
	"internal/poll"
	"io"
	"os"
)

// /sendFile使用sendFile 
// 系统调用将r的内容复制到c以最小化复制。
// 
// 如果handled==true，sendFile将返回复制的字节数和任何
// 非EOF错误。
// 
// 如果handled==false，则sendFile未执行任何工作。
func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
	// FreeBSD、DragonFly和Solaris使用0作为“直到EOF”值。
	// 如果您传入的字节数超过文件包含的字节数，它将
	// 以令人厌恶的方式循环回开始部分，直到它被发送
	// 确切的字节数。因此，我们需要知道要发送多少字节。
	var remain int64 = 0

	lr, ok := r.(*io.LimitedReader)
	if ok {
		remain, r = lr.N, lr.R
		if remain <= 0 {
			return 0, nil, true
		}
	}
	f, ok := r.(*os.File)
	if !ok {
		return 0, nil, false
	}

	if remain == 0 {
		fi, err := f.Stat()
		if err != nil {
			return 0, err, false
		}

		remain = fi.Size()
	}

	// FreeBSD/DragonFly/Solaris的sendfile 
	// 实现的另一个怪癖是它不使用文件的当前位置
	// 如果传递偏移量0，它将从
	// 偏移量0开始。没有办法告诉它“从当前
	// /位置开始”，所以我们必须明确管理它。
	pos, err := f.Seek(0, io.SeekCurrent)
	if err != nil {
		return 0, err, false
	}

	sc, err := f.SyscallConn()
	if err != nil {
		return 0, nil, false
	}

	var werr error
	err = sc.Read(func(fd uintptr) bool {
		written, werr = poll.SendFile(&c.pfd, int(fd), pos, remain)
		return true
	})
	if err == nil {
		err = werr
	}

	if lr != nil {
		lr.N = remain - written
	}

	_, err1 := f.Seek(written, io.SeekCurrent)
	if err1 != nil && err == nil {
		return written, err1, written > 0
	}

	return written, wrapSyscallError("sendfile", err), written > 0
}
